package com.by.toolkit.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.by.toolkit.consts.GlobalConst;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;

/**
 * 流处理工具类
 *
 * @author liujun
 * @date 2023年06月12 4:01 PM
 */
public class ByListUtil {

    private static final int DEFAULT_BATCH_SIZE = 1000;

    private ByListUtil() {

    }

    /**
     * 返回collection的stream，如果为空返回空stream
     *
     * @param collection 集合
     * @param <E>        泛型对象
     * @return stream
     */
    public static <E> Stream<E> getNullAbleStream(Collection<E> collection) {
        if (collection == null) {
            return Stream.<E>builder().build();
        }
        return collection.stream();
    }

    /**
     * list为空，则返回new ArrayList
     *
     * @param list 列表
     * @param <E>  泛型
     * @return list
     */
    public static <E> List<E> orElseNewList(List<E> list) {
        if (list == null) {
            return new ArrayList<>();
        }
        return list;
    }

    /**
     * 获取 分片后的stream
     *
     * @param list list
     * @param size 分片长度
     * @param <E>  泛型
     * @return stream
     */
    public static <E> Stream<List<E>> getPartitionStream(List<E> list, int size) {
        return Lists.partition(orElseNewList(list), size).stream();
    }

    public static <E> E getFirstObj(List<E> list) {
        return getObjByIndex(list, 0);
    }

    public static <E> E getLastObj(List<E> list) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        return getObjByIndex(list, list.size() - 1);
    }

    public static <E> E getObjByIndex(List<E> list, Integer index) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }

        if (index < GlobalConst.ZERO_LONG || index >= list.size()) {
            return null;
        }
        return list.get(index);
    }

    public static <E, R extends Comparable<? super R>> void distinct(Collection<E> list, Function<E, R>... distinctFieldFunction) {

        Map<String, E> distinceOptionalMap = list.stream().collect(Collectors.toMap(item -> {
            List<R> ukList = Arrays.stream(distinctFieldFunction).map(i -> i.apply(item)).collect(toList());
            return SplitterUtil.EN_EQ_JOINER.join(ukList);
        }, Function.identity(), (o, v) -> o));
        list.clear();
        list.addAll(distinceOptionalMap.values());
    }

    public static <E, R extends Comparable<? super R>, C extends Comparable<? super C>> Collection<E> distinctGetSortMax(Collection<E> list, Function<E, C> sortMaxFieldFunction, Function<E, R>... distinctFieldFunction) {

        Map<String, Optional<E>> optionalMap = list.stream().collect(Collectors.groupingBy(item -> {
            List<R> ukList = Arrays.stream(distinctFieldFunction).map(i -> i.apply(item)).collect(toList());
            return SplitterUtil.EN_EQ_JOINER.join(ukList);
        }, Collectors.maxBy(Comparator.comparing(sortMaxFieldFunction))));
        list.clear();
        list.addAll(
                optionalMap.values().stream()
                        .filter(Optional::isPresent)
                        .map(Optional::get)
                        .collect(toList()));
        return list;
    }

    public static <T> List<List<T>> subToList(List<T> list, int batchSize) {
        if (CollectionUtils.isEmpty(list)) {
            return emptyList();
        }

        return executeSubToList(list, batchSize > 0 ? batchSize : DEFAULT_BATCH_SIZE);
    }


    private static <T> List<List<T>> executeSubToList(List<T> list, int batchSize) {
        int size = list.size();
        int total = size % batchSize == 0 ? size / batchSize : size / batchSize + 1;

        return IntStream.range(0, total)
                .mapToObj(i -> {
                    int start = i * batchSize;
                    int end = (i + 1) * batchSize;
                    return list.subList(start, Math.min(end, size));
                })
                .collect(toList());
    }

    public static <T,R> Boolean repeatFlag(List<T> list, Function<T,R> function) {
        return CollectionUtil.distinct(list,function,false).size() != list.size();
    }
}
